package com.siashan.toolkit.api.security.advice;


import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.siashan.toolkit.api.security.anno.ApiSignEnable;
import com.siashan.toolkit.core.wrapper.AjaxResult;
import com.siashan.toolkit.api.security.properties.RestCryptoProperties;
import com.siashan.toolkit.api.security.properties.SignProperties;
import com.siashan.toolkit.api.security.util.sign.SignUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Map;

/**
 * 接口返回数据加密
 *
 * @author siashan
 * @since V1.0.1
 **/
@ControllerAdvice
@Slf4j
public class RestCryptoResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    /**
     * 是否执行加签
     */
    private boolean encrypt;

    @Autowired
    private RestCryptoProperties restCryptoProperties;
    @Autowired
    private ObjectMapper objectMapper;


    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
        /**
         * 接口返回数据加签需同时满足以下三个条件
         * 1、配置文件接口返回数据加签开关开启
         * 2、接口有CryptoEnable注解
         * 3、CryptoEnable注解 outEncode属性为true
         */
        SignProperties sign = restCryptoProperties.getSign();
        if (null != sign && sign.isOutSign()) {
            if (methodParameter.getMethod().isAnnotationPresent(ApiSignEnable.class)) {
                ApiSignEnable cryptoEnable = methodParameter.getMethodAnnotation(ApiSignEnable.class);
                if (cryptoEnable.outSign()) {
                    encrypt = true;
                }
            }
        }
        return encrypt;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

        // 如果接口返回数据不需要加签直接返回原报文
        if (!encrypt) {
            return body;
        }

        // 如果接口返回数据格式不是 AjaxResult 直接放过
        if (!(body instanceof AjaxResult)) {
            return body;
        }

        //只针对Result的data进行加密
        AjaxResult result = (AjaxResult) body;

        // 如果报文体为null则不执行加签
        Object data = result.getData();
        if (null == data) {
            return body;
        }

        String dataStr = "";
        Class<?> dataClass = data.getClass();

        try {
            if (dataClass.isPrimitive() || (data instanceof String)) {
                dataStr = String.valueOf(data);
            } else {
                dataStr = objectMapper.writeValueAsString(data);
            }
            AjaxResult cloneResult = new AjaxResult();
            BeanUtil.copyProperties(result, cloneResult);
            cloneResult.setData(dataStr);
            Map<String, Object> map = BeanUtil.beanToMap(cloneResult);

            String signture = SignUtil.generateSignature(map, restCryptoProperties);
            result.setSign(signture);


        }  catch (Exception e) {
            log.warn("接口返回数据加签异常",e);
            return AjaxResult.error("服务器内部异常，请稍后重试");
        }
        return result;
    }
}
